home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Sample PCI sound input hardware driver
- **
- ** by Mark Cookson, Apple Developer Technical Support
- **
- ** File: PCISoundInputDriver.c
- **
- ** Copyright ©1996 Apple Computer, Inc.
- ** All rights reserved.
- **
- ** You may incorporate this sample code into your applications without
- ** restriction, though the sample code has been provided "AS IS" and the
- ** responsibility for its operation is 100% yours. However, what you are
- ** not permitted to do is to redistribute the source as "Apple Sample
- ** Code" after having made changes. If you're going to re-distribute the
- ** source, we require that you make it clear in the source that the code
- ** was descended from Apple Sample Code, but that you've made changes.
- */
-
- #include <Aliases.h>
- #include <DriverServices.h>
- #include <DriverGestalt.h>
- #include <NameRegistry.h>
- #include <Resources.h>
- #include <Sound.h>
- #include <SoundInput.h>
- #include <TextUtils.h>
- #include <Timer.h>
-
- #include <stdio.h>
- #include <Strings.h>
-
- // This is what our input parameter looks like
- #pragma options align=mac68k
- struct SoundParam {
- QElemPtr qLink;
- short qType;
- short ioTrap;
- Ptr ioCmdAddr;
- IOCompletionUPP ioCompletion;
- OSErr ioResult;
- StringPtr ioNamePtr;
- short ioVRefNum;
- short ioCRefNum;
- short csCode;
- short csParam[11];
- };
- typedef struct SoundParam SoundParam, *SoundParamPtr;
- #pragma options align=reset
-
- // tell the OS all about us
-
- extern DriverDescription TheDriverDescription = {
- kTheDescriptionSignature,
- kInitialDriverDescriptor,
- // DriverType
- "\pTestSoundInputDriver",
- 1, 0, developStage, 1,
- // DriverOSRuntimeInfo
- 0
- | (1 * kDriverIsLoadedUponDiscovery) /* Loader runtime options */
- | (1 * kDriverIsOpenedUponLoad) /* Opened when loaded */
- | (0 * kDriverIsUnderExpertControl) /* No I/O expert to handle loads/opens */
- | (0 * kDriverIsConcurrent) /* Not concurrent yet */
- | (0 * kDriverQueuesIOPB), /* Not internally queued yet */
- "\p.TestSoundInputDriver", /* Str31 driverName (OpenDriver param) */
- 0, 0, 0, 0, 0, 0, 0, 0, /* UInt32 driverDescReserved[8] */
- // DriverOSService
- 1, /* ServiceCount nServices */
- // DriverServiceInfo
- kServiceCategoryNdrvDriver, /* OSType serviceCategory */
- kNdrvTypeIsGeneric, /* OSType serviceType */
- 1, 0, developStage, 1, /* major, minor, stage, rev */
- };
-
- //For debugging information
- #define DEBUG 0
- #define DEBUG2 0
- #define FULLDEBUG 0
-
- #define kSamplesInBuffer 1024
- #define kSilence 128
- #define kAmplitude 75
-
- #define kSquareSource 1
- #define kSawSource 2
- #define kSilenceSource 3
-
- #define kNumRates 3
- #define kNumSizes 2
- #define kNumCompressions 1
-
- typedef struct myTMTask {
- TMTask theTask;
- ParmBlkPtr pb;
- } myTMTask;
-
-
- //----------------------------------------------------------------------------------
- // Function Prototypes
- //----------------------------------------------------------------------------------
-
- pascal OSErr FragInit (CFragInitBlockPtr initInfo);
- pascal void FragTerm (void);
-
- static OSStatus DriverKillIOCmd (ParmBlkPtr pb);
- extern OSErr DoDriverIO (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind);
-
- static pascal void HWIntProc (QElemPtr passedPtr);
- static void MakeSquareWave (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels);
- static void MakeSawWave (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels);
- static OSErr DoOptionsDialog (void);
- static void SetHardwareToDefault (void);
-
-
- //----------------------------------------------------------------------------------
- // Globals
- //----------------------------------------------------------------------------------
-
- //Hardware specific globals
- SInt16 gContinuousOn,
- gLevelMeterOn,
- gTwosComplementOn,
- gAGCOn,
- gPlayThruVolume,
- gVoxRecordingOn,
- gVoxStoppingOn,
- gPauseState,
- gNumberChannels,
- gSampleSize,
- gVOXStartTrigger,
- gVOXStopTrigger,
- gVOXDelay,
- gCompressionFactor,
- gCurrentInputSource = kSquareSource;
- SInt32 gActiveChannels; //channels to record from 0x01 == only left, 0x02 == only right, 0x03 == both channels
- UnsignedFixed gSampleRate;
- Fixed gInputGain,
- gLeftInputGain,
- gRightInputGain;
- OSType gCompressionType,
- gRecordingQuality;
- SIInterruptUPP gUserInterruptProc = nil;
-
- //Globals needed to simulate our hardware
- myTMTask gSimulatedHWInterrupt;
- TimerUPP gSimulatedHWIntProc = nil;
- SInt32 gInterruptFreq = 0,
- gSamplesWritten = 0;
- SInt16 gNextSaw16Sample = -((kAmplitude * 0.01) * 32767); //starting point for saw tooth wave
- SInt8 gNextSaw8Sample = kSilence - kAmplitude; //starting point for saw tooth wave
- Ptr gSoundBuffer = nil; //our hardware's internal buffer
-
- //Driver specific globals
- Boolean gStopRecording = false;
- DriverRefNum gDrvrRefNum;
- Handle gDrvrIcon,
- gInputSourceNames;
- AliasHandle gAliasToDriver;
- StringHandle gDriverName;
-
- //----------------------------------------------------------------------------------
- // CFM Init
- //----------------------------------------------------------------------------------
-
- pascal OSErr FragInit (CFragInitBlockPtr initInfo) {
- Handle iconHandle,
- strHandle;
- StringHandle driverName;
- SInt16 resRefNum;
- OSErr err = noErr;
-
- if (initInfo->fragLocator.where == kOnDiskFlat || initInfo->fragLocator.where == kOnDiskSegmented) {
- resRefNum = FSpOpenResFile (initInfo->fragLocator.u.onDisk.fileSpec, fsRdPerm);
- err = ResError();
- if (err != noErr) {
- #if DEBUG
- SysDebugStr ("\p!!Error returned from FSpOpenResFile!!");
- #endif
- }
-
- if (resRefNum == -1) {
- #if DEBUG
- SysDebugStr ("\p!!Error resRefNum is -1!!");
- #endif
- }
- }
-
- if (resRefNum != -1 && err == noErr) {
- iconHandle = Get1Resource ('ICN#', 128);
- err = ResError ();
- if (iconHandle == nil) {
- #if DEBUG
- SysDebugStr ("\p!!iconHandle is nil!!");
- #endif
- }
-
- if (err != noErr) {
- #if DEBUG
- SysDebugStr ("\p!!Error from Get1Resource('ICN#', 128)!!");
- #endif
- }
-
- if (iconHandle != nil) {
- gDrvrIcon = NewHandleSys (GetHandleSize (iconHandle));
- BlockMoveData (*iconHandle, *gDrvrIcon, GetHandleSize (iconHandle));
- ReleaseResource (iconHandle);
- } else if (err == noErr) {
- err = resNotFound;
- #if DEBUG
- SysDebugStr ("\p!!Error Get1Resource('ICN#', 128), resNotFound!!");
- #endif
- }
-
- strHandle = Get1Resource ('STR#', 128);
- err = ResError ();
- if (strHandle == nil) {
- #if DEBUG
- SysDebugStr ("\p!!strHandle is nil!!");
- #endif
- }
-
- if (err != noErr) {
- #if DEBUG
- SysDebugStr ("\p!!Error from Get1Resource('STR#', 128)!!");
- #endif
- }
-
- if (strHandle != nil) {
- gInputSourceNames = NewHandleSys (GetHandleSize (strHandle));
- BlockMoveData (*strHandle, *gInputSourceNames, GetHandleSize (strHandle));
- ReleaseResource (strHandle);
- } else if (err == noErr) {
- err = resNotFound;
- #if DEBUG
- SysDebugStr ("\p!!Error Get1Resource('STR#', 128), resNotFound!!");
- #endif
- }
-
- driverName = GetString (128);
- err = ResError ();
- if (driverName == nil) {
- #if DEBUG
- SysDebugStr ("\p!!driverName is nil!!");
- #endif
- }
-
- if (err != noErr) {
- #if DEBUG
- SysDebugStr ("\p!!Error from GetString(128)!!");
- #endif
- }
-
- if (driverName != nil) {
- gDriverName = (StringHandle)NewHandleSys (GetHandleSize ((Handle)driverName));
- BlockMoveData (*driverName, *gDriverName, GetHandleSize ((Handle)driverName));
- ReleaseResource ((Handle)driverName);
- } else if (err == noErr) {
- err = resNotFound;
- #if DEBUG
- SysDebugStr ("\p!!Error GetString(128), resNotFound!!");
- #endif
- }
-
- CloseResFile (resRefNum);
- }
-
- if (err == noErr) {
- //make an alias to ourselves so that we can open our driver later if needed
- err = NewAlias (nil, initInfo->fragLocator.u.onDisk.fileSpec, &gAliasToDriver);
- }
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in FragInit!!");
- }
- #endif
-
- return (err);
- }
-
- pascal void FragTerm (void) {
- if (gDrvrIcon != nil) {
- DisposeHandle (gDrvrIcon);
- gDrvrIcon = nil;
- }
-
- if (gInputSourceNames != nil) {
- DisposeHandle (gInputSourceNames);
- gInputSourceNames = nil;
- }
-
- if (gAliasToDriver != nil) {
- DisposeHandle ((Handle)gAliasToDriver);
- gAliasToDriver = nil;
- }
- }
-
- //----------------------------------------------------------------------------------
- // Driver calls
- //----------------------------------------------------------------------------------
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverInitializeCmd (AddressSpaceID addressSpaceID, DriverInitInfoPtr initialInfo) {
- #pragma unused (addressSpaceID, initialInfo)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverInitializeCmd;g");
- #endif
-
- gDrvrRefNum = initialInfo->refNum;
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverInitializeCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverInitializeCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo) {
- #pragma unused (finalInfo)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverFinalizeCmd;g");
- #endif
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverFinalizeCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverFinalizeCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo) {
- #pragma unused (supersededInfo)
-
- OSErr err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverSupersededCmd;g");
- #endif
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverSupersededCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverSupersededCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverReplaceCmd (AddressSpaceID addressSpaceID, DriverReplaceInfoPtr replaceInfo) {
- #pragma unused (addressSpaceID, replaceInfo)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverReplaceCmd;g");
- #endif
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverReplaceCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverReplaceCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverOpenCmd (AddressSpaceID addressSpaceID, ParmBlkPtr pb) {
- #pragma unused (addressSpaceID, pb)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverOpenCmd;g");
- #endif
-
- gSoundBuffer = NewPtrSys (kSamplesInBuffer * 4); //room for 16 bit stereo samples
-
- if (gSoundBuffer != nil) {
- gSimulatedHWIntProc = NewTimerProc (HWIntProc);
- SetHardwareToDefault ();
-
- err = SPBSignInDevice (gDrvrRefNum, *gDriverName);
- } else {
- #if DEBUG
- SysDebugStr ("\p!!couldn't allocate memory for our buffer!!");
- #endif
- err = MemError ();
- }
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverOpenCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverOpenCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- Always run at task level, can allocate and move memory.
- */
- static OSStatus DriverCloseCmd (ParmBlkPtr pb) {
- #pragma unused (pb)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverCloseCmd;g");
- #endif
-
- (void)SPBSignOutDevice (gDrvrRefNum);
- DisposePtr (gSoundBuffer);
- gSoundBuffer = nil;
- DisposeRoutineDescriptor (gSimulatedHWIntProc);
- gSimulatedHWIntProc = nil;
-
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverCloseCmd;g");
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- static OSStatus DriverControlCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) {
- #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
- OSStatus err = noErr;
- OSType selector = 0;
-
- #if FULLDEBUG
- SysDebugStr ("\pin DriverControlCmd;g");
- #endif
-
- if (pb->csCode == 2) {
- selector = ((OSType*)pb->csParam)[0];
- }
-
- switch (selector) {
- //Control calls that are required to be supported
- case siCompressionType:
- //Set the compression type. Some devices allow the incoming samples to be
- //compressed before being placed in your application’s input buffer. The
- //infoData parameter points to a buffer of type OSType, which is the
- //compression type.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siCompressionType");
- #endif
- if (((OSType*)pb->csParam)[1] != 'NONE') {
- err = siInvalidCompression; //we don't support compression
- }
- break;
- case siContinuous:
- //Set the state of continuous recording from this device. If recording
- //is being turned off, the driver stops recording samples to its internal buffer.
- //Only sound input device drivers that support asynchronous recording support
- //continuous recording. The infoData parameter points to an integer, which is
- //the state of continuous recording (0 is off, 1 is on).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siContinuous");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gContinuousOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
- break;
- case siLevelMeterOnOff:
- //Set the current state of the level meter. For calls to set the level meter,
- //the infoData parameter points to an integer that indicates whether the level meter
- //is off (0) or on (1).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siLevelMeterOnOff");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gLevelMeterOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
- break;
- case siNumberChannels:
- //Set the number of channels this device is to record. The infoData parameter points
- //to an integer, which indicates the number of channels. Note that this selector
- //determines the format of the data stream output by the driver. If the number of
- //channels is 1, the driver should output monophonic data in response to a Read call.
- //If the number of channels is 2, the driver should output interleaved stereo data.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siNumberChannels");
- #endif
- if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 2) {
- gNumberChannels = ((SInt16*)pb->csParam)[2];
- } else {
- err = notEnoughHardwareErr;
- }
- break;
- case siRecordingQuality:
- //Set the current quality of recorded sound. The infoData parameter points to a buffer
- //of type OSType, which is the recording quality. Currently foud qualities are supported,
- //defined by these constants:
- // siCDQuality = 'cd ', /*44.1kHz, stereo, 16 bit*/
- // siBestQuality = 'best'; /*22kHz, mono, 8 bit*/
- // siBetterQuality = 'betr'; /*22kHz, mono, MACE 3:1*/
- // siGoodQuality = 'good';
- //These qualities are defined by the sound input device driver. Usually best means
- //monaural, 8-bit, 22 kHz, sound with no compression.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siRecordingQuality");
- #endif
- switch (((OSType*)pb->csParam)[1]) {
- case siCDQuality:
- gRecordingQuality = siCDQuality;
- gSampleRate = rate44khz;
- gSampleSize = 16;
- gNumberChannels = 2;
- gCompressionType = 'NONE';
- break;
- case siBestQuality:
- gRecordingQuality = siBestQuality;
- gSampleRate = rate22050hz;
- gSampleSize = 8;
- gNumberChannels = 1;
- gCompressionType = 'NONE';
- break;
- case siBetterQuality:
- gRecordingQuality = siBetterQuality;
- gSampleRate = rate22050hz;
- gSampleSize = 8;
- gNumberChannels = 1;
- gCompressionType = 'NONE';
- break;
- case siGoodQuality:
- gRecordingQuality = siGoodQuality;
- gSampleRate = rate11025hz;
- gSampleSize = 8;
- gNumberChannels = 1;
- gCompressionType = 'NONE';
- break;
- default:
- err = siUnknownQuality;
- }
- break;
- case siSampleRate:
- //Set the sample rate to be produced by this device. The sample rate must be in the
- //range 0 to 65535.65535 Hz. The sample rate is declared as a Fixed data type. In
- //order to accommodate sample rates greater than 32 kHz, the most significant bit is
- //not treated as a sign bit; instead, that bit is interpreted as having the value 32,768.
- //The infoData parameter points to a buffer of type Fixed, which is the sample rate.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siSampleRate");
- #endif
- if (((UnsignedFixed*)pb->csParam)[1] == rate44khz || ((UnsignedFixed*)pb->csParam)[1] == rate22050hz || ((UnsignedFixed*)pb->csParam)[1] == rate11025hz) {
- gSampleRate = ((UnsignedFixed*)pb->csParam)[1];
- } else {
- err = siInvalidSampleRate;
- }
- break;
- case siSampleSize:
- //Set the sample size to be produced by this device. Because some compression formats
- //require specific sample sizes, this selector might return an error when compression
- //is used. The infoData parameter points to an integer, which is the sample size.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siSampleSize");
- #endif
- if (((SInt16*)pb->csParam)[2] == 8 || ((SInt16*)pb->csParam)[2] == 16) {
- gSampleSize = ((SInt16*)pb->csParam)[2];
- } else {
- err = siInvalidSampleSize;
- }
- break;
- case siTwosComplementOnOff:
- //Set the current state of the two’s complement feature. This selector only applies to
- //8-bit data. (16-bit samples are always stored in two’s complement format.) If on, the
- //driver stores all samples in the application buffer as two’s complement values
- //(that is, –128 to 127). Otherwise, the driver stores the samples as offset binary
- //values (that is, 0 to 255). The infoData parameter points to an integer, which is the
- //current state of the two’s complement feature (1 if two’s complement output is desired,
- //0 otherwise).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siTwosComplementOnOff");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gTwosComplementOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
- break;
-
- //Sound Manager only calls
- case siCloseDriver:
- //The Sound Input Manager sends this selector when it closes a device previously
- //opened with write permission. The sound input device driver should stop any
- //recording in progress, deallocate the input hardware, and initialize local
- //variables to default settings.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siCloseDriver");
- #endif
- //We will actually stop the hardware at the next hardware interrupt
- gStopRecording = true;
- break;
- case siInitializeDriver:
- //The Sound Input Manager sends this selector when it opens a sound input device
- //with write permission. The sound input device driver initializes local variables
- //and prepares to start recording. If possible, the driver initializes the device
- //to a sampling rate of 22 kHz, a sample size of 8 bits, mono recording, no compression,
- //automatic gain control on, and all other features off.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siInitializeDriver");
- #endif
- SetHardwareToDefault ();
- break;
- case siPauseRecording:
- //The Sound Input Manager uses this selector to set the current pause state.
- //The sound input device driver continues recording but does not store the sampled
- //data in a buffer. The infoData parameter points to an integer, which indicates
- //the state of pausing (0 is off, 1 is on).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siPauseRecording");
- #endif
- gPauseState = ((SInt16*)pb->csParam)[2];
- break;
- case siUserInterruptProc:
- //The Sound Input Manager sends this selector to specify the sound input interrupt
- //routine that the sound input device driver should call. The infoData parameter
- //points to a procedure pointer, which is the address of the sound input interrupt routine.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siUserInterruptProc");
- #endif
- gUserInterruptProc = ((SIInterruptUPP*)pb->csParam)[1];
- break;
-
- //Control calls that can be optionally supported
- case siActiveChannels:
- //Set the channels to record from. When setting the active channels, the data passed
- //in is a long integer that is interpreted as a bitmap describing the channels to record
- //from. For example, if bit 0 is set, then the first channel is made active. The samples
- //for each active channel are interleaved in the application’s buffer.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siActiveChannels");
- #endif
- if (((SInt32*)pb->csParam)[1] >= 0x01 && ((SInt32*)pb->csParam)[1] <= 0x03) {
- gActiveChannels = ((SInt32*)pb->csParam)[1];
- } else {
- err = notEnoughHardwareErr;
- }
- break;
- case siAGCOnOff:
- //Set the current state of the automatic gain control feature. The infoData parameter
- //points to an integer, which is 0 if gain control is off and 1 if it is on.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siAGCOnOff");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gAGCOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
- break;
- case siInputGain:
- //Set the current sound input gain. If the available hardware allows adjustment of the
- //recording gain, this selector lets you get and set the gain. In response to a Control
- //call, a sound input driver sets the gain level used for all subsequent recording to the
- //specified value. The infoData parameter points to a 4-byte value of type Fixed ranging
- //from 0.5 to 1.5, where 1.5 specifies maximum gain.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siInputGain");
- #endif
- if (((Fixed*)pb->csParam)[1] >= (Fixed)0.5 && ((Fixed*)pb->csParam)[1] <= (Fixed)1.5) {
- gInputGain = ((Fixed*)pb->csParam)[1];
- } else {
- err = paramErr;
- }
- break;
- case siInputSource:
- //Set the current sound input source. If the available hardware allows recording from more
- //than one source, this selector lets you get and set the source. In response to a Control
- //call, a sound input driver sets the source of all subsequent recording to the value passed
- //in. If the value is less than 1 or greater than the number of input sources, the driver
- //returns paramErr; if the driver supports only one source, it returns siUnknownInfoType. The
- //infoData parameter points to an integer, which is the index of the current sound input source.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siInputSource");
- #endif
- if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 3) {
- gCurrentInputSource = ((SInt16*)pb->csParam)[2];
- } else {
- err = notEnoughHardwareErr;
- }
- break;
- case siOptionsDialog:
- //Cause the driver to display the Options dialog box (SPBSetDeviceInfo). This dialog box
- //is designed to allow the user to configure device-specific features of the sound input
- //hardware. With SPBSetDeviceInfo, the infoData parameter is unused.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siOptionsDialog");
- #endif
- err = DoOptionsDialog ();
- break;
- case siPlayThruOnOff:
- //Set the current play-through state and volume. The infoData parameter points to an integer,
- //which indicates the current play-through volume (1 to 7). If that integer is 0, then
- //play-through is off.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siPlayThruOnOff");
- #endif
- err = notEnoughHardwareErr;
- // If our (fake) hardware actually did playthrough, this would be useful...
- /*
- if (((short*)pb->csParam)[2] >= 0 || ((short*)pb->csParam)[2] <= 7) {
- gPlayThruVolume = ((short*)pb->csParam)[2];
- } else {
- err = notEnoughHardwareErr;
- }
- */
- break;
- case siStereoInputGain:
- //Set the current stereo sound input gain. If the available hardware allows adjustment of
- //the recording gain, this selector lets you get and set the gain for each of two channels
- //(left or right). In response to a Status call, a sound input driver should return the
- //current gain setting for the specified channel. In response to a Control call, a sound input
- //driver should set the gain level used for all subsequent recording to the specified value.
- //The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5,
- //where 1.5 specifies maximum gain. The first of these values is equivalent to the gain for
- //the left channel and the second value is equivalent to the gain for the right channel.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siStereoInputGain");
- #endif
- if (((Fixed*)pb->csParam)[1] <= 1.5 && ((Fixed*)pb->csParam)[1] >= 0.5 && ((Fixed*)pb->csParam)[2] <= 1.5 && ((Fixed*)pb->csParam)[2] >= 0.5) {
- gLeftInputGain = ((Fixed*)pb->csParam)[1];
- gRightInputGain = ((Fixed*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
- break;
- case siVoxRecordInfo:
- //Set the current VOX recording parameters. The infoData parameter points to two integers.
- //The first integer indicates whether VOX recording is on or off (0 if off, 1 if on). The
- //second integer indicates the VOX record trigger value. Trigger values range from 0 to 255
- //(0 is trigger immediately, 255 is trigger only on full volume).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siVoxRecordInfo");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gVoxRecordingOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
-
- if (gVoxRecordingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) {
- gVOXStartTrigger = ((SInt16*)pb->csParam)[3];
- } else {
- err = paramErr;
- }
- break;
- case siVoxStopInfo:
- //Set the current VOX stopping parameters. The infoData parameter points to three integers.
- //The first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The
- //second integer indicates the VOX stop trigger value. Trigger values range from 0 to 255
- //(255 is stop immediately, 0 is stop only on total silence). The third integer indicates
- //how many milliseconds the trigger value must be continuously valid for recording to be stopped.
- //Delay values range from 0 to 65,535.
- #if DEBUG2
- SysDebugStr ("\pcontrol: siVoxStopInfo");
- #endif
- if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) {
- gVoxStoppingOn = ((SInt16*)pb->csParam)[2];
- } else {
- err = paramErr;
- }
-
- if (gVoxStoppingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) {
- gVOXStopTrigger = ((SInt16*)pb->csParam)[3];
- gVOXDelay = ((SInt16*)pb->csParam)[4];
- } else {
- err = paramErr;
- }
- break;
-
- //Unknown or unsupported control call
- default:
- #if DEBUG
- SysDebugStr ("\pcontrol: unknown selector");
- #endif
- err = controlErr;
- }
-
- #if DEBUG
- if (err != noErr && err != controlErr) {
- SysDebugStr ("\p!!Error in DriverControlCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverControlCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- static OSStatus DriverStatusCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) {
- #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
- OSStatus err = noErr;
- OSType selector = 0;
- Handle iconHandle,
- compressionAvailable,
- sampleRateAvailable,
- sampleSizeAvailable;
- SoundInfoList info;
-
- #if FULLDEBUG
- SysDebugStr ("\pin DriverStatusCmd;g");
- #endif
-
- if (pb->csCode == 2) {
- selector = ((OSType*)pb->csParam)[0];
- }
-
- switch (selector) {
- //Status calls that are required to be supported
- case siAsync:
- //Determine whether the driver supports asynchronous recording functions. The infoData parameter
- //points to an integer, which is 0 if the driver supports synchronous calls only and 1 otherwise.
- //Some sound input drivers do not support asynchronous recording at all, and some might support
- //asynchronous recording only on certain hardware configurations.
- #if DEBUG2
- SysDebugStr ("\pstatus: siAsync");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 1; //we can do async recording
- break;
- case siChannelAvailable:
- //Get the maximum number of channels this device can record. The infoData parameter points to an
- //integer, which is the number of available channels.
- #if DEBUG2
- SysDebugStr ("\pstatus: siChannelAvailable");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 2; //we can do stereo (note: Sound Manager doesn't currently work with more than 2 channels)
- break;
- case siCompressionAvailable:
- //Get the number and list of compression types this device can produce. The infoData parameter
- //points to an integer, which is the number of compression types, followed by a handle. The
- //handle references a list of compression types, each of type OSType.
- #if DEBUG2
- SysDebugStr ("\pstatus: siCompressionAvailable");
- #endif
- compressionAvailable = NewHandle (sizeof (OSType) * kNumCompressions);
- if (compressionAvailable != nil) {
- ((OSType*)*compressionAvailable)[0] = 'NONE';
- info.count = kNumCompressions;
- info.infoHandle = compressionAvailable;
- ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned
- *(SoundInfoList*)(pb->csParam+2) = info;
- }
- break;
- case siCompressionFactor:
- //Get the compression factor of the current compression type. For example, the compression factor
- //for MACE 3:1 compression is 3. If a sound input device driver supports only compression type 'NONE',
- //the returned compression type is 1. The infoData parameter points to an integer, which is the
- //compression factor.
- #if DEBUG2
- SysDebugStr ("\pstatus: siCompressionFactor");
- #endif
- if (gCompressionType == 'NONE') {
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 1; //no compression equals 1:1
- }
- break;
- case siCompressionType:
- //Get the compression type. Some devices allow the incoming samples to be compressed before being
- //placed in your application’s input buffer. The infoData parameter points to a buffer of type
- //OSType, which is the compression type.
- #if DEBUG2
- SysDebugStr ("\pstatus: siCompressionType");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((OSType*)pb->csParam)[1] = gCompressionType;
- break;
- case siContinuous:
- //Get the state of continuous recording from this device. If recording is being turned off, the
- //driver stops recording samples to its internal buffer. Only sound input device drivers that
- //support asynchronous recording support continuous recording. The infoData parameter points to an
- //integer, which is the state of continuous recording (0 is off, 1 is on).
- #if DEBUG2
- SysDebugStr ("\pstatus: siContinuous");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gContinuousOn;
- break;
- case siDeviceBufferInfo:
- //Get the size of the device’s internal buffer. This information can be useful when you want to
- //modify sound input data at interrupt time. Note, however, that if a driver is recording continuously,
- //then the size of the buffer passed to your sound input interrupt routine might be greater than the
- //size this selector returns because data recorded between calls to SPBRecord as well as recorded
- //during calls to SPBRecord will be sent to your interrupt routine. The infoData parameter points to
- //a long integer, which is the size of the device’s internal buffer.
- #if DEBUG2
- SysDebugStr ("\pstatus: siDeviceBufferInfo");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((SInt32*)pb->csParam)[1] = kSamplesInBuffer * gNumberChannels * (gSampleSize / 8);
- break;
- case siDeviceConnected:
- //Get the state of the device connection. The infoData parameter points to an integer, which is one
- //of the following constants:
- // siDeviceIsConnected = 1;
- // siDeviceNotConnected = 0;
- // siDontKnowIfConnected = -1;
- //The siDeviceIsConnected constant indicates that the device is connected and ready. The
- //siDeviceNotConnected constant indicates that the device is not connected. The siDontKnowIfConnected
- //constant indicates that the Sound Input Manager cannot determine whether the device is connected.
- #if DEBUG2
- SysDebugStr ("\pstatus: siDeviceConnected");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = siDeviceIsConnected;
- break;
- case siDeviceIcon:
- //Get the device’s icon and icon mask. In response to a Status call, a sound input device driver
- //should return, in the location specified by the infoData parameter, a handle to a block of memory
- //that contains the icon and its mask in the format of an 'ICN#' resource. It is the driver’s
- //responsibility to allocate that block of memory, but it should not releasee it. The software issuing
- //this selector is responsible for disposing of the handle. As a result, a device driver should detach
- //any resource handles (by calling DetachResource) before returning them to the caller.
- #if DEBUG2
- SysDebugStr ("\pstatus: siDeviceIcon");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- iconHandle = gDrvrIcon;
- err = HandToHand (&iconHandle);
- if (err != noErr) {
- SysDebugStr ("\pHandToHand returned an error");
- }
- ((Handle*)pb->csParam)[1] = iconHandle;
- break;
- case siDeviceName:
- //Get the name of the sound input device. Your application must pass a pointer to a buffer that will
- //be filled in with the device’s name. The buffer needs to be large enough to hold a Str255 data type.
- #if DEBUG2
- SysDebugStr ("\pstatus: siDeviceName");
- #endif
- ((SInt32*)pb->csParam)[0] = 0; //Sound Manager doesn't have to copy any data
- BlockMoveData (gDriverName[0], ((Handle*)pb->csParam)[1], *gDriverName[0]+1);
- break;
- case siLevelMeterOnOff:
- //Get the current state of the level meter. To get the level meter setting, the infoData parameter
- //points to two integers; the first integer indicates the state of the level meter, and the second
- //integer contains the level value of the meter. The level meter setting is an integer that ranges
- //from 0 (no volume) to 255 (full volume).
- #if DEBUG2
- SysDebugStr ("\pstatus: siLevelMeterOnOff");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gLevelMeterOn;
- ((SInt16*)pb->csParam)[3] = 0;
- break;
- case siNumberChannels:
- //Get the number of channels this device is to record. The infoData parameter points to an integer,
- //which indicates the number of channels. Note that this selector determines the format of the data
- //stream output by the driver. If the number of channels is 1, the driver should output monophonic data
- //in response to a Read call. If the number of channels is 2, the driver should output interleaved
- //stereo data.
- #if DEBUG2
- SysDebugStr ("\pstatus: siNumberChannels");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gNumberChannels;
- break;
- case siRecordingQuality:
- //Get the current quality of recorded sound. The infoData parameter points to a buffer of type
- //OSType, which is the recording quality. Currently four qualities are supported, defined by
- //these constants:
- // siCDQuality = 'cd ', /*44.1kHz, stereo, 16 bit*/
- // siBestQuality = 'best'; /*22kHz, mono, 8 bit*/
- // siBetterQuality = 'betr'; /*22kHz, mono, MACE 3:1*/
- // siGoodQuality = 'good';
- //These qualities are defined by the sound input device driver. Usually best means monaural,
- //8-bit, 22 kHz, sound with no compression.
- #if DEBUG2
- SysDebugStr ("\pstatus: siRecordingQuality");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((OSType*)pb->csParam)[1] = gRecordingQuality;
- break;
- case siSampleRate:
- //Get the sample rate to be produced by this device. The sample rate must be in the range 0 to
- //65535.65535 Hz. The sample rate is declared as a Fixed data type. In order to accommodate sample
- //rates greater than 32 kHz, the most significant bit is not treated as a sign bit; instead, that
- //bit is interpreted as having the value 32,768. The infoData parameter points to a buffer of type
- //Fixed, which is the sample rate.
- #if DEBUG2
- SysDebugStr ("\pstatus: siSampleRate");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((UnsignedFixed*)pb->csParam)[1] = gSampleRate;
- break;
- case siSampleRateAvailable:
- //Get the range of sample rates this device can produce. The infoData parameter points to an integer,
- //which is the number of sample rates the device supports, followed by a handle. The handle references
- //a list of sample rates, each of type Fixed. If the device can record a range of sample rates, the
- //number of sample rates is set to 0 and the handle contains two rates, the minimum and the maximum
- //of the range of sample rates. Otherwise, a list is returned that contains the sample rates supported.
- //In order to accommodate sample rates greater than 32 kHz, the most significant bit is not treated as
- //a sign bit; instead, that bit is interpreted as having the value 32,768.
- #if DEBUG2
- SysDebugStr ("\pstatus: siSampleRateAvailable");
- #endif
- sampleRateAvailable = NewHandle (sizeof (UnsignedFixed) * kNumRates);
- if (sampleRateAvailable != nil) {
- ((UnsignedFixed*)*sampleRateAvailable)[0] = rate44khz;
- ((UnsignedFixed*)*sampleRateAvailable)[1] = rate22050hz;
- ((UnsignedFixed*)*sampleRateAvailable)[2] = rate11025hz;
- info.count = kNumRates;
- info.infoHandle = sampleRateAvailable;
- ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned
- *(SoundInfoList*)(pb->csParam+2) = info;
- }
- break;
- case siSampleSize:
- //Get the sample size to be produced by this device. Because some compression formats require specific
- //sample sizes, this selector might return an error when compression is used. The infoData parameter
- //points to an integer, which is the sample size.
- #if DEBUG2
- SysDebugStr ("\pstatus: siSampleSize");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gSampleSize;
- break;
- case siSampleSizeAvailable:
- //Get the range of sample sizes this device can produce. The infoData parameter points to an integer,
- //which is the number of sample sizes the device supports, followed by a handle. The handle references
- //a list of sample sizes, each of type Integer.
- #if DEBUG2
- SysDebugStr ("\pstatus: siSampleSizeAvailable");
- #endif
- sampleSizeAvailable = NewHandle (sizeof (SInt16) * kNumSizes);
- if (sampleSizeAvailable != nil) {
- ((SInt16*)*sampleSizeAvailable)[0] = 8;
- ((SInt16*)*sampleSizeAvailable)[1] = 16;
- info.count = kNumSizes;
- info.infoHandle = sampleSizeAvailable;
- ((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned
- *(SoundInfoList*)(pb->csParam+2) = info;
- }
- break;
- case siTwosComplementOnOff:
- //Get the current state of the two’s complement feature. This selector only applies to 8-bit data.
- //(16-bit samples are always stored in two’s complement format.) If on, the driver stores all samples
- //in the application buffer as two’s complement values (that is, –128 to 127). Otherwise, the driver
- //stores the samples as offset binary values (that is, 0 to 255). The infoData parameter points to an
- //integer, which is the current state of the two’s complement feature (1 if two’s complement output
- //is desired, 0 otherwise).
- #if DEBUG2
- SysDebugStr ("\pstatus: siTwosComplementOnOff");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gTwosComplementOn;
- break;
-
- //Sound Manager only call
- case siPauseRecording:
- //The Sound Input Manager uses this selector to get the current pause state.
- //The sound input device driver continues recording but does not store the sampled
- //data in a buffer. The infoData parameter points to an integer, which indicates
- //the state of pausing (0 is off, 1 is on).
- #if DEBUG2
- SysDebugStr ("\pcontrol: siPauseRecording");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gPauseState;
- break;
-
- //Status calls that can be optionally supported
- case siActiveChannels:
- //Get the channels to record from. When reading the active channels, the data returned is
- //a bitmap of the active channels.
- #if DEBUG2
- SysDebugStr ("\pstatus: siActiveChannels");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((SInt32*)pb->csParam)[1] = gActiveChannels;
- break;
- case siActiveLevels:
- //Get the current signal level for each active channel. The infoData parameter points to an array
- //of integers, the size of which depends on the number of active channels. You can determine how
- //many channels are active by calling SPBGetDeviceInfo with the siNumberChannels selector.
- #if DEBUG2
- SysDebugStr ("\pstatus: siActiveLevels");
- #endif
- if (gNumberChannels == 1) {
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 0;
- } else if (gNumberChannels == 2) {
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 0;
- ((SInt16*)pb->csParam)[3] = 0;
- }
- break;
- case siAGCOnOff:
- //Get the current state of the automatic gain control feature. The infoData parameter points to an
- //integer, which is 0 if gain control is off and 1 if it is on.
- #if DEBUG2
- SysDebugStr ("\pstatus: siAGCOnOff");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gAGCOn;
- break;
- case siCompressionHeader:
- //Get a compressed sound header for the current recording settings. Your application passes in a
- //pointer to a compressed sound header and the driver fills it in. Before calling SPBGetDeviceInfo
- //with this selector, you should set the numFrames field of the compressed sound header to the number
- //of bytes in the sound. When SPBGetDeviceInfo returns successfully, that field contains the number
- //of sample frames in the sound. This selector is needed only by drivers that use compression types
- //that are not directly supported by Apple. If you call this selector after recording a sound, your
- //application can get enough information about the sound to play it or save it in a file. The infoData
- //parameter points to a compressed sound header.
- #if DEBUG2
- SysDebugStr ("\pstatus: siCompressionHeader");
- #endif
- err = siUnknownInfoType; //we don't support compression
- break;
- case siCompressionNames:
- //Get a list of names of the compression types supported by the sound input device. In response to a
- //Status call, a sound input device driver returns, in the location specified by the infoData parameter,
- //a handle to a block of memory that contains the names of all compression types supported by the driver.
- //It is the driver’s responsibility to allocate that block of memory, but it should not release it. The
- //software issuing this selector is responsible for disposing of the handle. As a result, a device driver
- //must detach any resource handles (by calling DetachResource) before returning them to the caller. The
- //data in the handle has the same format as an 'STR#' resource: a two-byte count of the strings in the
- //resource, followed by the strings themselves. The strings should occur in the same order as the
- //compression types returned by the siCompressionAvailable selector. If the driver does not support
- //compression, it returns siUnknownInfoType. If the driver supports compression but for some reason not
- //all compression types are currently selectable, it returns a list of all available compression types.
- #if DEBUG2
- SysDebugStr ("\pstatus: siCompressionNames");
- #endif
- err = siUnknownInfoType; //we don't support compression
- break;
- case siInputGain:
- //Get the current sound input gain. If the available hardware allows adjustment of the recording gain,
- //this selector lets you get and set the gain. In response to a Status call, a sound input driver returns
- //the current gain setting. The infoData parameter points to a 4-byte value of type Fixed ranging from
- //0.5 to 1.5, where 1.5 specifies maximum gain.
- #if DEBUG2
- SysDebugStr ("\pstatus: siInputGain");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((Fixed*)pb->csParam)[1] = gInputGain;
- break;
- case siInputSource:
- //Get the current sound input source. If the available hardware allows recording from more than one
- //source, this selector lets you get and set the source. In response to a Status call, a sound input
- //driver returns the current source value; if the driver supports only one source, it returns
- //siUnknownInfoType.
- #if DEBUG2
- SysDebugStr ("\pstatus: siInputSource");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gCurrentInputSource;
- break;
- case siInputSourceNames:
- //Get a list of the names of all the sound input sources supported by the sound input device. In
- //response to a Status call, a sound input device driver returns, in the location specified by the
- //infoData parameter, a handle to a block of memory that contains the names of all sound sources
- //supported by the driver. It is the driver’s responsibility to allocate that block of memory, but it
- //should not release it. The software issuing this selector is responsible for disposing of the handle.
- //As a result, a device driver must detach any resource handles (by calling DetachResource) before
- //returning them to the caller. The data in the handle has the same format as an 'STR#' resource: a
- //two-byte count of the strings in the resource, followed by the strings themselves. The strings should
- //occur in the same order as the input sources returned by the siInputSource selector. If the driver
- //supports only one source, it returns siUnknownInfoType. If the driver supports more than one source
- //but for some reason not all of them are currently selectable, it returns a list of all available
- //input sources.
- #if DEBUG2
- SysDebugStr ("\pstatus: siInputSourceNames");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((Handle*)pb->csParam)[1] = gInputSourceNames;
- break;
- case siOptionsDialog:
- //Determine whether the driver supports an Options dialog box (SPBGetDeviceInfo). This dialog box is
- //designed to allow the user to configure device-specific features of the sound input hardware. With
- //SPBGetDeviceInfo, the infoData parameter points to an integer, which indicates whether the driver
- //supports an Options dialog box (1 if it supports it, 0 otherwise).
- #if DEBUG2
- SysDebugStr ("\pstatus: siOptionsDialog");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = 1; //we have a dialog
- break;
- case siPlayThruOnOff:
- //Get the current play-through state and volume. The infoData parameter points to an integer,
- //which indicates the current play-through volume (1 to 7). If that integer is 0, then
- //play-through is off.
- #if DEBUG2
- SysDebugStr ("\pstatus: siPlayThruOnOff");
- #endif
- ((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gPlayThruVolume;
- break;
- case siStereoInputGain:
- //Get the current stereo sound input gain. If the available hardware allows adjustment of the recording
- //gain, this selector lets you get and set the gain for each of two channels (left or right). In response
- //to a Status call, a sound input driver should return the current gain setting for the specified channel.
- //The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5, where 1.5
- //specifies maximum gain. The first of these values is equivalent to the gain for the left channel and the
- //second value is equivalent to the gain for the right channel.
- #if DEBUG2
- SysDebugStr ("\pstatus: siStereoInputGain");
- #endif
- ((SInt32*)pb->csParam)[0] = 8; //number of bytes being returned
- ((Fixed*)pb->csParam)[1] = gLeftInputGain;
- ((Fixed*)pb->csParam)[2] = gRightInputGain;
- break;
- case siVoxRecordInfo:
- //Get the current VOX recording parameters. The infoData parameter points to two integers. The first
- //integer indicates whether VOX recording is on or off (0 if off, 1 if on). The second integer indicates
- //the VOX record trigger value. Trigger values range from 0 to 255 (0 is trigger immediately, 255 is
- //trigger only on full volume).
- #if DEBUG2
- SysDebugStr ("\pstatus: siVoxRecordInfo");
- #endif
- ((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gVoxRecordingOn;
- ((SInt16*)pb->csParam)[3] = gVOXStartTrigger;
- break;
- case siVoxStopInfo:
- //Get the current VOX stopping parameters. The infoData parameter points to three integers. The
- //first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The second integer
- //indicates the VOX stop trigger value. Trigger values range from 0 to 255 (255 is stop immediately, 0
- //is stop only on total silence). The third integer indicates how many milliseconds the trigger value must
- //be continuously valid for recording to be stopped. Delay values range from 0 to 65,535.
- #if DEBUG2
- SysDebugStr ("\pstatus: siVoxStopInfo");
- #endif
- ((SInt32*)pb->csParam)[0] = 6; //number of bytes being returned
- ((SInt16*)pb->csParam)[2] = gVoxStoppingOn;
- ((SInt16*)pb->csParam)[3] = gVOXStopTrigger;
- ((SInt16*)pb->csParam)[4] = gVOXDelay;
- break;
-
- //Unknown or unsupported status call
- default:
- #if DEBUG
- SysDebugStr ("\pstatus: unknown selector");
- #endif
- err = statusErr;
- }
-
- #if DEBUG
- if (err != noErr && err != statusErr && err != siUnknownInfoType) {
- SysDebugStr ("\p!!Error in DriverStatusCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverStatusCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- static OSStatus DriverReadCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) {
- #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
- OSStatus err = noErr;
- double_t samplesInBuffer = kSamplesInBuffer,
- temp, temp2;
-
- #if DEBUG
- SysDebugStr ("\pin DriverReadCmd;g");
- #endif
-
- gSimulatedHWInterrupt.theTask.tmAddr = gSimulatedHWIntProc;
- gSimulatedHWInterrupt.theTask.tmWakeUp = 0;
- gSimulatedHWInterrupt.pb = pb;
-
- temp = ((gSampleRate >> 16) / samplesInBuffer);
-
- temp2 = -(1000000.0 / temp); //calculate time in microseconds
- gInterruptFreq = temp2;
-
- InsXTime ((QElemPtr)&gSimulatedHWInterrupt);
- PrimeTime ((QElemPtr)&gSimulatedHWInterrupt, gInterruptFreq); //wait for the time it takes to "record" one buffer
-
- err = ioInProgress;
-
- #if DEBUG
- if (err != noErr && err != ioInProgress) {
- SysDebugStr ("\p!!Error in DriverReadCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverReadCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- static OSStatus DriverWriteCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) {
- #pragma unused (addressSpaceID, ioCommandID, ioCommandKind, pb)
- OSStatus err;
-
- #if DEBUG
- SysDebugStr ("\pin DriverWriteCmd;g");
- #endif
-
- //Sound input drivers don't have a write command.
- err = paramErr;
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverWriteCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverWriteCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- static OSStatus DriverKillIOCmd (ParmBlkPtr pb) {
- #pragma unused (pb)
- OSStatus err = noErr;
-
- #if DEBUG
- SysDebugStr ("\pin DriverKillIOCmd");
- #endif
-
- gStopRecording = true; //when our next hardware "interrupt" fires we will stop the "hardware"
-
- #if DEBUG
- if (err != noErr) {
- SysDebugStr ("\p!!Error in DriverKillIOCmd!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DriverKillIOCmd;g");
- #endif
- #endif
-
- return (err);
- }
-
- /*
- May run at interrupt level, CANNOT allocate or move memory.
- */
- extern OSErr DoDriverIO(AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind) {
- OSStatus status;
- OSErr err = noErr;
-
- #if FULLDEBUG
- SysDebugStr ("\pin DoDriverIO;g");
- #endif
-
- /*
- Note: Initialize, Open, KillIO, Close, and Finalize are either synchronous
- or immediate. Read, Write, Control, and Status may be immediate,
- synchronous, or asynchronous.
- */
- switch (ioCommandCode) {
- case kInitializeCommand: /* Always immediate */
- status = DriverInitializeCmd (addressSpaceID, ioCommandContents.initialInfo);
- break;
- case kFinalizeCommand: /* Always immediate */
- status = DriverFinalizeCmd (ioCommandContents.finalInfo);
- break;
- case kSupersededCommand: /* Always immediate */
- status = DriverSupersededCmd (ioCommandContents.supersededInfo);
- break;
- case kReplaceCommand: /* Always immediate, replace an old driver */
- status = DriverReplaceCmd (addressSpaceID, ioCommandContents.replaceInfo);
- break;
- case kOpenCommand: /* Always immediate */
- status = DriverOpenCmd (addressSpaceID, ioCommandContents.pb);
- break;
- case kCloseCommand: /* Always immediate */
- status = DriverCloseCmd (ioCommandContents.pb);
- break;
- case kControlCommand:
- status = DriverControlCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb);
- break;
- case kStatusCommand:
- status = DriverStatusCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb);
- break;
- case kReadCommand:
- status = DriverReadCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb);
- break;
- case kWriteCommand:
- status = DriverWriteCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb);
- break;
- case kKillIOCommand: /* Always immediate */
- status = DriverKillIOCmd (ioCommandContents.pb);
- break;
- default:
- status = paramErr;
- break;
- }
- if ((ioCommandKind & kImmediateIOCommandKind) != 0) {
- /* Immediate commands return the operation status and don't call IOCommandIsComplete */
- } else if (status == ioInProgress) {
- /* Perform the action and call IOCommandIsComplete at some later time */
- status = noErr;
- } else {
- err = status;
- status = IOCommandIsComplete (ioCommandID, err);
- }
-
- #if DEBUG
- if (err == paramErr) {
- SysDebugStr ("\p!!Param Error in DoDriverIO!!");
- }
- #if FULLDEBUG
- SysDebugStr ("\pleaving DoDriverIO;g");
- #endif
- #endif
-
- return (status);
- }
-
- static pascal void HWIntProc (QElemPtr passedPtr) {
- UInt32 numSamples,
- numBytesLeftToRecord;
- Boolean recordingComplete = false;
- ParmBlkPtr pb = ((myTMTask*)passedPtr)->pb;
-
- #if DEBUG
- SysDebugStr ("\pin HWIntProc");
- #endif
-
- if (gStopRecording == false) {
- numBytesLeftToRecord = pb->ioParam.ioReqCount - pb->ioParam.ioActCount;
-
- //If the number of bytes requested is larger than our buffer, return the max number of samples that will fit in our buffer
- if (numBytesLeftToRecord > (kSamplesInBuffer * gNumberChannels * (gSampleSize/8))) {
- numSamples = kSamplesInBuffer;
- } else {
- //otherwise figure out how many samples the requested number of bytes equals
- numSamples = numBytesLeftToRecord / (gNumberChannels * (gSampleSize/8));
- }
-
- switch (gCurrentInputSource) {
- case 1:
- //Make a square wave
- MakeSquareWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels);
- break;
- case 2:
- //Make a saw tooth wave
- MakeSawWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels);
- break;
- case 3:
- //Make silence
- MakeSquareWave (0, numSamples, gSampleRate, 0, gSampleSize, gNumberChannels);
- break;
- default:
- #if DEBUG
- SysDebugStr ("\punknown input source in HWIntProc");
- #endif
- break; //null statement so when DEBUG is false this switch still compiles
- }
-
- if (gTwosComplementOn == true && gSampleSize == 8) {
- SInt32 i;
- for (i = 0; i < numSamples; i++) {
- ((UInt32*)gSoundBuffer)[i] ^= 0x80808080;
- }
- }
-
- if (gUserInterruptProc != nil) {
- CallSIInterruptProc (gUserInterruptProc, pb, gSoundBuffer, 0, gSampleSize);
- }
-
- //if we have a buffer and pausing is off, copy data to app's buffer
- if (pb->ioParam.ioBuffer != nil && gPauseState == 0) {
- BlockMoveData (gSoundBuffer, pb->ioParam.ioBuffer+pb->ioParam.ioActCount, numSamples*(gSampleSize/8)*gNumberChannels);
- }
-
- //If we are not paused, update the number of samples we have made
- if (gPauseState == 0) {
- pb->ioParam.ioActCount += numSamples*(gSampleSize/8)*gNumberChannels;
- }
-
- if (pb->ioParam.ioActCount == pb->ioParam.ioReqCount) {
- recordingComplete = true;
- }
- }
-
- if (recordingComplete == false && gStopRecording == false) {
- PrimeTime ((QElemPtr)passedPtr, gInterruptFreq); //more data to record
- } else {
- //Have recorded requested number of bytes, stop the hardware (if continuous recording is not on)
- //Do a software interrupt so that we can call IOCommandIsComplete (which can only be called at
- //task level and software interrupt level.
- RmvTime ((QElemPtr)passedPtr);
- if (gStopRecording == true) {
- SetHardwareToDefault ();
- } else if (gContinuousOn == false) {
- gSamplesWritten = 0;
- }
- IOCommandIsComplete ((IOCommandID)pb->ioParam.ioCmdAddr, noErr);
- }
-
- #if FULLDEBUG
- SysDebugStr ("\pleaving HWIntProc;g");
- #endif
- }
-
- //Makes a square wave of the specified frequency and duration
- //duration in 1/100 of a second, i.e. 500 for a five second tone
- //frequency in samples per second, i.e. 8000 for an 8000Hz square wave (requires 16kHz sampling rate)
- static void MakeSquareWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) {
- SInt32 numSamples = 0;
-
- if (duration != 0 && numberSamples == 0) {
- numSamples = (duration / 100.0) * (sampleRate >> 16);
- } else if (duration == 0 && numberSamples != 0) {
- numSamples = numberSamples;
- }
-
- if (numSamples > 0) {
- if (frequency == 0) {
- //Generate silence of requested length
- if (sampleSize == 8) {
- UInt8 *buffer8 = (UInt8*)gSoundBuffer;
- UInt8 *end8 = buffer8 + (numSamples * numChannels);
- while (buffer8 < end8) {
- *buffer8++ = kSilence;
- }
- } else if (sampleSize == 16) {
- UInt16 *buffer16 = (UInt16*)gSoundBuffer;
- UInt16 *end16 = buffer16 + (numSamples * numChannels);
- while (buffer16 < end16) {
- *buffer16++ = 0;
- }
- } else {
- #if DEBUG
- SysDebugStr ("\pbad sample size in MakeSquareWave (silence)");
- #endif
- }
- } else if ((frequency * 2) <= sampleRate) {
- //Generate a square wave
- SInt32 samplesBeforeReverse = (((sampleRate >> 16) / frequency) / 2) * numChannels;
-
- if (sampleSize == 8) {
- UInt8 *buffer8 = (UInt8*)gSoundBuffer;
- UInt8 *end8 = buffer8 + (numSamples * numChannels);
- while (buffer8 < end8) {
- if (gSamplesWritten < samplesBeforeReverse) {
- *buffer8++ = kSilence + kAmplitude;
- gSamplesWritten++;
- } else {
- *buffer8++ = kSilence - kAmplitude;
- if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
- gSamplesWritten = 0;
- } else {
- gSamplesWritten++;
- }
- }
- }
- } else if (sampleSize == 16) {
- UInt16 *buffer16 = (UInt16*)gSoundBuffer;
- UInt16 *end16 = buffer16 + (numSamples * numChannels);
- while (buffer16 < end16) {
- if (gSamplesWritten < samplesBeforeReverse) {
- *buffer16++ = (kAmplitude * 0.01) * 32767;
- gSamplesWritten++;
- } else {
- *buffer16++ = -((kAmplitude * 0.01) * 32767);
- if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
- gSamplesWritten = 0;
- } else {
- gSamplesWritten++;
- }
- }
- }
- } else {
- #if DEBUG
- SysDebugStr ("\pbad sample size in MakeSquareWave (square wave)");
- #endif
- }
- }
- }
- }
-
- static void MakeSawWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) {
- SInt32 numSamples = 0;
-
- if (duration != 0 && numberSamples == 0) {
- numSamples = (duration / 100.0) * (sampleRate >> 16);
- } else if (duration == 0 && numberSamples != 0) {
- numSamples = numberSamples;
- }
-
- if (numSamples > 0) {
- if ((frequency * 2) <= sampleRate) {
- //Generate a saw tooth wave
- SInt32 samplesBeforeReverse = (((sampleRate >> 16) / frequency) / 2);
-
- if (sampleSize == 8) {
- UInt8 *buffer8 = (UInt8*)gSoundBuffer;
- UInt8 *end8 = buffer8 + (numSamples * numChannels);
- UInt8 increment = (kAmplitude*2) / samplesBeforeReverse;
- while (buffer8 < end8) {
- if (gSamplesWritten < samplesBeforeReverse) {
- *buffer8++ = gNextSaw8Sample;
- if (numChannels == 2) {
- *buffer8++ = gNextSaw8Sample;
- }
- gNextSaw8Sample += increment;
- gSamplesWritten++;
- } else {
- *buffer8++ = gNextSaw8Sample;
- if (numChannels == 2) {
- *buffer8++ = gNextSaw8Sample;
- }
- gNextSaw8Sample -= increment;
- if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
- gSamplesWritten = 0;
- } else {
- gSamplesWritten++;
- }
- }
- }
- } else if (sampleSize == 16) {
- UInt16 *buffer16 = (UInt16*)gSoundBuffer;
- UInt16 *end16 = buffer16 + (numSamples * numChannels);
- UInt16 increment = (((kAmplitude * 0.01) * 32767)*2) / samplesBeforeReverse;
- while (buffer16 < end16) {
- if (gSamplesWritten < samplesBeforeReverse) {
- *buffer16++ = gNextSaw16Sample;
- if (numChannels == 2) {
- *buffer16++ = gNextSaw16Sample;
- }
- gNextSaw16Sample += increment;
- gSamplesWritten++;
- } else {
- *buffer16++ = gNextSaw16Sample;
- if (numChannels == 2) {
- *buffer16++ = gNextSaw16Sample;
- }
- gNextSaw16Sample -= increment;
- if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) {
- gSamplesWritten = 0;
- } else {
- gSamplesWritten++;
- }
- }
- }
- } else {
- #if DEBUG
- SysDebugStr ("\pbad sample size in MakeSawWave");
- #endif
- }
- }
- }
- }
-
- static OSErr DoOptionsDialog (void) {
- FSSpec driverFSSpec;
- Rect rect;
- ControlHandle item = nil;
- DialogRef optionsDialog;
- OSErr err;
- SInt16 itemHit = 0,
- itemType,
- curRes,
- resRef = -1,
- newInputSource = gCurrentInputSource;
- Boolean wasChanged,
- done = false;
-
- curRes = CurResFile ();
- err = ResolveAlias (nil, gAliasToDriver, &driverFSSpec, &wasChanged);
- if (err == noErr) {
- resRef = FSpOpenResFile (&driverFSSpec, fsRdPerm);
- if (resRef != -1) {
- optionsDialog = GetNewDialog (128, nil, (GrafPtr)-1L);
- } else {
- #if DEBUG
- SysDebugStr ("\pFSpOpenResFile failed");
- #endif
- }
- } else {
- #if DEBUG
- SysDebugStr ("\pResolveAlias failed");
- #endif
- }
-
- if (optionsDialog != nil) {
- //gCurrentInputSource+5 is the item number of the corresponding radio buttons
- //so itemHit-5 is the input source number
- GetDialogItem (optionsDialog, gCurrentInputSource+5, &itemType, &(Handle)item, &rect);
- if (item != nil) {
- SetControlValue (item, 1);
- SetDialogDefaultItem (optionsDialog, 1); //put the ring around the OK button
- } else {
- #if DEBUG
- SysDebugStr ("\pitem is nil!");
- #endif
- }
-
- ShowWindow (optionsDialog);
-
- do {
- ModalDialog (nil, &itemHit);
- switch (itemHit) {
- case 1: //OK button
- gCurrentInputSource = newInputSource;
- done = true;
- break;
- case 2: //Cancel button
- done = true;
- break;
- case 6: //Square wave
- case 7: //Saw wave
- case 8: //Silence
- newInputSource = itemHit-5;
- GetDialogItem (optionsDialog, 6, &itemType, &(Handle)item, &rect);
- SetControlValue (item, itemHit == 6);
- GetDialogItem (optionsDialog, 7, &itemType, &(Handle)item, &rect);
- SetControlValue (item, itemHit == 7);
- GetDialogItem (optionsDialog, 8, &itemType, &(Handle)item, &rect);
- SetControlValue (item, itemHit == 8);
- break;
- }
- } while (!done);
-
- DisposeDialog (optionsDialog);
- optionsDialog = nil;
- } else {
- #if DEBUG
- SysDebugStr ("\poptionsDialog is nil!");
- #endif
- }
-
- if (resRef != -1) {
- CloseResFile (resRef);
- UseResFile (curRes);
- }
-
- return err;
- }
-
- static void SetHardwareToDefault (void) {
- gContinuousOn = 0;
- gLevelMeterOn = 0;
- gTwosComplementOn = 0;
- gAGCOn = 0;
- gPlayThruVolume = 0;
- gVoxRecordingOn = 0;
- gVoxStoppingOn = 0;
- gPauseState = 0;
- gNumberChannels = 1;
- gSampleSize = 8;
- gVOXStartTrigger = 0;
- gVOXStopTrigger = 0;
- gVOXDelay = 0;
- gCompressionFactor = 1;
- // gCurrentInputSource = 1; //don't change the current input source
- gActiveChannels = 0x00000001;
- gSampleRate = rate22050hz;
- gInputGain = 0x00010000;
- gLeftInputGain = 0x01000100;
- gRightInputGain = 0x01000100;
- gCompressionType = 'NONE';
- gRecordingQuality = siBestQuality;
- gUserInterruptProc = nil;
- gSamplesWritten = 0;
- gNextSaw8Sample = kSilence - kAmplitude;
- gNextSaw16Sample = -((kAmplitude * 0.01) * 32767);
- gStopRecording = false;
- }
-
-